package com.zhejiangzhengyuan.municipal_common.config;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhejiangzhengyuan.municipal_common.entity.WarnMessage;
import com.zhejiangzhengyuan.municipal_common.entity.vo.MessageDetail;
import com.zhejiangzhengyuan.municipal_common.entity.vo.MessageVo;
import com.zhejiangzhengyuan.municipal_common.service.WarnMessageService;
import com.zhejiangzhengyuan.municipal_common.utils.MyApplicationContextAware;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * @author lin_ju
 * @create on 2019/12/3
 */
@ServerEndpoint("/messageSocket/{moduleName}/{userInfoId}")
@Component
@Slf4j
public class MessageServer {
  // concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
  private static CopyOnWriteArraySet<MessageServer> webSocketSet = new CopyOnWriteArraySet<>();

  // 由于ServerEndpoint 每次连接  无法Autowired
  //  @Autowired private WarnMessageService messageService;

  @Autowired private WarnMessageService messageService;

  // 与某个客户端的连接会话，需要通过它来给客户端发送数据
  private Session session;

  // 接收userId
  private Long userInfoId;
  // 接收模块名称
  private String moduleName;
  /** 连接建立成功调用的方法 */
  @OnOpen
  public void onOpen(
      Session session,
      @PathParam("moduleName") String moduleName,
      @PathParam("userInfoId") Long userInfoId) {
    this.session = session;
    this.userInfoId = userInfoId;
    this.moduleName = moduleName;

    webSocketSet.add(this); // 加入set中

    log.info("有新窗口开始监听:{}", userInfoId);

    if (Objects.isNull(messageService)) {
      messageService =
          (WarnMessageService)
              MyApplicationContextAware.getApplicationContext().getBean("warnMessageServiceImpl");
    }
    try {
      sendObjectMessage(this, getMessageVo(moduleName, userInfoId));
    } catch (IOException e) {
      log.error("websocket IO异常");
    }
  }

  /** 连接关闭调用的方法 */
  @OnClose
  public void onClose() {
    webSocketSet.remove(this); // 从set中删除
    log.info("有一连接关闭");
  }

  /**
   * 收到客户端消息后调用的方法
   *
   * @param message 客户端发送过来的消息
   */
  @OnMessage
  public void onMessage(String message, Session session) {
    log.info("收到来自窗口" + userInfoId + "的信息:" + message);
    // do noting
  }

  /**
   * @param session 连接产生的会话
   * @param error 发生的异常
   */
  @OnError
  public void onError(Session session, Throwable error) {
    log.error("发生错误");
    error.printStackTrace();
  }

  /**
   * 服务端广播接口
   *
   * @param message 具体message
   * @throws IOException io异常
   */
  public void sendStringMessage(String message) throws IOException {
    this.session.getBasicRemote().sendText(message);
  }

  /**
   * @param moduleName 模块名
   * @param userInfoId 指定监听端口
   * @param messageVo 信息实体
   * @throws IOException sendText 失败会爆出异常
   */
  public void sendObjectMessage(String moduleName, Long userInfoId, Object messageVo)
      throws IOException {
    for (MessageServer messageServer : webSocketSet) {
      if (messageServer.moduleName.equals(moduleName)
          && messageServer.userInfoId.equals(userInfoId)) {
        // 不支持object 转为Json
        messageServer.session.getBasicRemote().sendText(JSONObject.toJSONString(messageVo, true));
      }
    }
  }

  /**
   * 根据moduleName和userInfoId找到socket 刷新其消息列表
   *
   * @param moduleName 模块名
   * @param userInfoId user名
   */
  public void refreshSocketMessage(String moduleName, Long userInfoId) {
    for (MessageServer messageServer : webSocketSet) {
      if (messageServer.moduleName.equals(moduleName)
          && messageServer.userInfoId.equals(userInfoId)) {
        // 不支持object 转为Json
        try {
          sendObjectMessage(messageServer, getMessageVo(moduleName, userInfoId));
        } catch (IOException e) {
          log.error("websocket IO异常");
        }
      }
    }
  }

  /** 全局刷新消息 */
  public void refreshSocketMessage() {
    for (MessageServer messageServer : webSocketSet) {
      try {
        sendObjectMessage(
            messageServer, getMessageVo(messageServer.moduleName, messageServer.userInfoId));
      } catch (IOException e) {
        log.error("websocket IO异常");
      }
    }
  }

  /**
   * @param server 接收端口实体
   * @param messageVo 信息实体
   * @throws IOException sendText 失败会爆出异常
   */
  private void sendObjectMessage(MessageServer server, Object messageVo) throws IOException {
    server.session.getBasicRemote().sendText(JSONObject.toJSONString(messageVo, true));
  }

  /**
   * 服务端点对点发送消息接口
   *
   * @param message 具体消息
   * @param moduleName 子系统名称
   * @param userInfoId 端口标识
   * @throws IOException io异常
   */
  public static void sendInfo(String message, String moduleName, Long userInfoId)
      throws IOException {
    log.info("{}系统推送消息到{}=====>推送内容{}", moduleName, userInfoId, message);
    List<MessageServer> sendSocket; // 存放推送客户端
    if (Strings.isBlank(moduleName)) {
      sendSocket = new ArrayList<>(webSocketSet);
    } else {
      sendSocket =
          webSocketSet.stream()
              .filter(
                  messageServer ->
                      Strings.isNotBlank(messageServer.moduleName)
                          && messageServer.moduleName.equals(moduleName))
              .collect(Collectors.toList());
    }

    sendSocket.forEach(
        item -> {
          // 这里可以设定只推送给这个sid的，为null则全部推送
          try {
            if (userInfoId == null) {
              item.sendStringMessage(message);
            } else if (item.userInfoId.equals(userInfoId)) {
              item.sendStringMessage(message);
            }
          } catch (IOException e) {
            e.printStackTrace();
          }
        });
  }

  private MessageVo getMessageVo(String moduleName, Long userInfoId) {
    // 查询用户存在的数据
    QueryWrapper<WarnMessage> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("receive_user_id", userInfoId);
    if (!moduleName.equals("all")) {
      queryWrapper.eq("module_name", moduleName);
    }
    List<WarnMessage> messages = messageService.list(queryWrapper);
    MessageVo messageVo = new MessageVo();
    messageVo.setCount(messages.size());
    messages.forEach(
        message -> {
          MessageDetail detail = messageService.selectVo(message.getId());
          messageVo.addDetail(detail);
        });
    return messageVo;
  }
}
